/*
 * Partial3DMatcher.java Created on Dec 10, 2007 by M.C.Boulahiya
 */
package gui;

import java.awt.*;
import java.awt.event.*;

import javax.media.j3d.*;
import javax.swing.*;
import javax.vecmath.*;

import model.P3DMObjectFile;

import com.sun.j3d.loaders.Scene;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.universe.*;

/**
 * @author M.C.Boulahiya
 *
 */
public class Partial3DMatcher extends JFrame {
	/** The serial Version Number */
	private static final long serialVersionUID = 7413149200126500891L;
	private BoundingSphere boundingSphere = new BoundingSphere(new Point3d(), 1000.0);
	private BranchGroup objectsRoot;
	private TransformGroup examine;
	private SimpleUniverse simpleUniverse;
	//private int nbS3D = 0;

	
	/**
	 * @throws HeadlessException
	 */
	public Partial3DMatcher() throws HeadlessException {
	}

	/**
	 * @param gc
	 */
	public Partial3DMatcher(GraphicsConfiguration gc) {
		super(gc);
	}

	/**
	 * @param titre
	 * @param gc
	 */
	public Partial3DMatcher(String titre, GraphicsConfiguration gc) {
		super(titre, gc);
	}

	/**
	 * @param titre
	 * @throws HeadlessException
	 */
	public Partial3DMatcher(String titre) throws HeadlessException {
		super(titre);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setLayout(new BorderLayout());
		
    initSwingComponents();
		
    initJava3DComponents();
	}

	/**
	 * 
	 */
	private void initSwingComponents() {
		setJMenuBar(getP3DMMenuBar());
	}

	/**
	 * 
	 */
	private JMenuBar getP3DMMenuBar() {
		JMenuBar menuBar;
		JMenu fileMenu, exitMenu;
		JMenuItem menuItem;
		
		JPopupMenu.setDefaultLightWeightPopupEnabled( false );
		
		P3DMActionListener menuListener = new P3DMActionListener(this);
		
		fileMenu = new JMenu("Loading");
		menuItem = new JMenuItem("Obj ancient load");
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
		menuItem.addActionListener(menuListener);
		fileMenu.add(menuItem);
		menuItem = new JMenuItem("Obj quick load");
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.ALT_MASK));
		menuItem.addActionListener(menuListener);
		fileMenu.add(menuItem);
		
		exitMenu = new JMenu("Quit");
		menuItem = new JMenuItem("Exit");
		menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK));
		menuItem.addActionListener(menuListener);
		exitMenu.add(menuItem);
		
		menuBar = new JMenuBar();
		menuBar.add(fileMenu);
		menuBar.add(javax.swing.Box.createHorizontalGlue());
		menuBar.add(exitMenu);
		
		return menuBar;
	}

	/**
	 * 
	 */
	private void initJava3DComponents() {
		GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
		Canvas3D canvas3d = new Canvas3D(config);
		add("Center", canvas3d);
		
		simpleUniverse = new SimpleUniverse(canvas3d);
		
		PlatformGeometry platformGeometry = new PlatformGeometry();

		// Set up the background
		Color3f backgroundColor = new Color3f(0.9f, 0.9f, 0.9f);
		Background backgroundNode = new Background(backgroundColor);
		backgroundNode.setApplicationBounds(this.boundingSphere);
		platformGeometry.addChild(backgroundNode);

		// Set up the ambient light
		Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
		AmbientLight ambientLightNode = new AmbientLight(ambientColor);
		ambientLightNode.setInfluencingBounds(this.boundingSphere);
		platformGeometry.addChild(ambientLightNode);
		
		// Set up a directional light
		Color3f directionalColor = new Color3f(1.0f, 1.0f, 0.9f);
		Vector3f direction  = new Vector3f(4.0f, -7.0f, -12.0f);
		
		DirectionalLight light1 = new DirectionalLight(directionalColor, direction);
		light1.setInfluencingBounds(this.boundingSphere);
		platformGeometry.addChild(light1);
		
		ViewingPlatform viewingPlatform = simpleUniverse.getViewingPlatform();
		viewingPlatform.setPlatformGeometry(platformGeometry);
				
		// Set clipping distances in the canvas3D
		canvas3d.getView().setBackClipDistance(1000.0);
		canvas3d.getView().setFrontClipDistance(0.05);
		
//		canvas3d.getView().setMinimumFrameCycleTime(50);
		
		BranchGroup root = createSceneGraph();
		simpleUniverse.addBranchGraph(root);		
	}

	/**
	 * @return
	 */
	private BranchGroup createSceneGraph() {
		BranchGroup root = new BranchGroup();
		root.setName("root");
		createExamine();		
		root.addChild(examine);
		
		objectsRoot = new BranchGroup();
		objectsRoot.setCapability(BranchGroup.ALLOW_DETACH);
		objectsRoot.setName("objectsRoot");
		examine.addChild(objectsRoot);
	
		return root;
	}

	/**
	 * 
	 */
	private void createExamine() {
		examine = new TransformGroup();
		examine.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
		examine.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
		examine.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
		examine.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		examine.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);		
		
		// Create the extended rotate behavior node
		MouseRotate emrBehavior = new MouseRotate(examine);
		examine.addChild(emrBehavior);
		emrBehavior.setSchedulingBounds(this.boundingSphere);

		// Create the zoom behavior node
		MouseZoom mzBehavior = new MouseZoom(examine);
		examine.addChild(mzBehavior);
		mzBehavior.setSchedulingBounds(this.boundingSphere);
		
		// Create the translate behavior node
		MouseTranslate mtBehavior = new MouseTranslate(examine);
		examine.addChild(mtBehavior);
		mtBehavior.setSchedulingBounds(this.boundingSphere);
		
		examine.setName("examine");
	}

	public SimpleUniverse getSimpleUniverse() {
		return this.simpleUniverse;
	}

	/**
	 * @param filename the name of the ".obj" file.
	 * @param quick boolean to choose the loading method
	 * 		false = the ancient slow load.
	 *    true = the brand new quick load.
	 */
	protected void fileObjToGraph(String filename, boolean quick) {
		System.out.println(filename);
		int flags = P3DMObjectFile.RESIZE;
		flags |= P3DMObjectFile.RECENTER;
		float creaseAngle = (float) (60.0 * Math.PI / 180.0);
		
		P3DMObjectFile f = new P3DMObjectFile(flags, creaseAngle, quick);
		Scene scene = null;
//		if (filename.equalsIgnoreCase("/home/potier/Equipe/Maiza/DataBase/Potteries/ObjFiles/FScanD27.obj") ||
//				filename.equalsIgnoreCase("/home/potier/Equipe/Maiza/DataBase/Potteries/ObjFiles/ScanRitt5.obj") )		
//			f.setProfileExtraction(true);
		long time = System.currentTimeMillis();
		try {
			scene = f.load(filename);
		}
		catch (Exception exception) {
			//FileNotFoundException, ParsingErrorException, IncorrectFormatException
			System.err.println(exception);
			System.exit(1);
		}
		time = System.currentTimeMillis() - time;
		System.out.println("load entier: " + time + " ms");
		time = System.currentTimeMillis();
		
		double diagonal = f.getBoundigBoxDiagonal();
		ViewingPlatform viewingPlatform = simpleUniverse.getViewingPlatform();
		Transform3D t = new Transform3D();
		t.setTranslation(new Vector3d(0.,0.,3*diagonal));
		viewingPlatform.getViewPlatformTransform().setTransform(t);
		
		final BranchGroup sceneGroupObj = scene.getSceneGroup();
    // TODO refaire un truc qui obtient les noms ;)
//		// Generalement Je ne charge qu'un seul objet par fichier, ce qui signifie 
//		// que dans les getNamedObjects j'aurais une unique Shape3D!!!
//		// Neanmoins certains objets sont décomposés en plusieurs Shape3Ds ;) 
//		final String[] names;			
//		Enumeration<?> e = scene.getNamedObjects().keys();
//
//		nbS3D = 0;//nbS3D contiendra le nombre de Shape3D
//		TreeScan.findNode(sceneGroupObj,Shape3D.class,new ProcessNodeInterface() {
//			public boolean processNode(Node node) {
//				if (node instanceof Shape3D) {
////					System.out.println("Set name on object: "+node);
//					nbS3D++;
//				}
//				return true;    // Continue scanning
//			} // End of processNode(Node)
//		}, false, true); // End of TreeScan
//
//		if (scene.getNamedObjects().size() == 1) {
//			names = new String[1];
//			names[0] = (String) e.nextElement();
//		}
//		else if (scene.getNamedObjects().size() == 0) {
////			System.err.print("There is no names for Shape3Ds in this file!\n");
////			System.err.print("We can't set the Shape3ds real names\n");
//			String goodFilename = obtainName(filename);
//			names = new String[nbS3D];
//			for (int i = 0; i < nbS3D; i++)
//				names[i] = goodFilename+i;
//		}
//		else {
////			System.err.print("There is more than ONE Shape3D in this file!\n");
//			names = new String[scene.getNamedObjects().size()];
//			for (int i = 0; i < scene.getNamedObjects().size(); i++)
//				names[i] = (String) e.nextElement();
//		}
//		sceneGroupObj.setName("sceneGroupObj");
//		nbS3D = 0;
//		TreeScan.findNode(sceneGroupObj,Shape3D.class,new ProcessNodeInterface() {
//			public boolean processNode(Node node) {
//				if (node instanceof Shape3D) {
////					System.out.println("Set name on object: "+node);
//					node.setName(names[nbS3D]);
//					nbS3D++;
//				}
//				return true;    // Continue scanning
//			} // End of processNode(Node)
//		}, false, true); // End of TreeScan

		TransformGroup objectsScale = new TransformGroup();
		Transform3D t3d = new Transform3D();
		t3d.setScale(2);
		objectsScale.setTransform(t3d);
		objectsScale.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
		objectsScale.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
		objectsScale.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		objectsScale.setName("objectsScale");
		objectsScale.addChild(sceneGroupObj);	
		this.objectsRoot.addChild(objectsScale);

		TransformGroup rotateTG = new TransformGroup();
		rotateTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		rotateTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		rotateTG.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
		rotateTG.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
		rotateTG.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
		rotateTG.setName("rotateTG");
		this.objectsRoot.addChild(rotateTG);
	} // End of fileVrmlToGraph(String)


	private String obtainName(String filename) {
		String name = null;

		int i = filename.lastIndexOf('/');
		if (i > 0 &&  i < filename.length() - 1)
			name = filename.substring(i+1);

		return name;
	} // End of obtainName(String)
	
	protected void prepareActions() {
		this.objectsRoot.detach();
		this.objectsRoot.removeAllChildren();
	}
	
	protected void finishActions() {
		this.examine.setTransform(new Transform3D());
		this.examine.addChild(this.objectsRoot);
	}

}// End of class Partial3DMatcher

// End of file Partial3DMatcher.java